Package org.python.pydev.debug.model

Source Code of org.python.pydev.debug.model.AbstractDebugTarget

/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.debug.model;

import java.util.List;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IBreakpointManager;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchListener;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IMemoryBlock;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IThread;
import org.eclipse.debug.internal.ui.views.console.ProcessConsole;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.internal.console.IOConsolePartition;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.ui.views.tasklist.ITaskListResourceAdapter;
import org.python.pydev.core.ExtensionHelper;
import org.python.pydev.core.docutils.StringUtils;
import org.python.pydev.core.log.Log;
import org.python.pydev.debug.core.IConsoleInputListener;
import org.python.pydev.debug.core.PydevDebugPlugin;
import org.python.pydev.debug.core.PydevDebugPreferencesInitializer;
import org.python.pydev.debug.model.remote.AbstractDebuggerCommand;
import org.python.pydev.debug.model.remote.AbstractRemoteDebugger;
import org.python.pydev.debug.model.remote.RemoveBreakpointCommand;
import org.python.pydev.debug.model.remote.RunCommand;
import org.python.pydev.debug.model.remote.SendPyExceptionCommand;
import org.python.pydev.debug.model.remote.SetBreakpointCommand;
import org.python.pydev.debug.model.remote.SetPropertyTraceCommand;
import org.python.pydev.debug.model.remote.ThreadListCommand;
import org.python.pydev.debug.model.remote.VersionCommand;
import org.python.pydev.debug.ui.launching.PythonRunnerConfig;

import com.aptana.shared_core.string.FastStringBuffer;
import com.aptana.shared_core.structure.Tuple;

/**
* This is the target for the debug (
*
* @author Fabio
*/
@SuppressWarnings("restriction")
public abstract class AbstractDebugTarget extends AbstractDebugTargetWithTransmission implements IDebugTarget,
        ILaunchListener, IExceptionsBreakpointListener, IPropertyTraceListener {

    private static final boolean DEBUG = false;

    /**
     * Path pointing to the file that started the debug (e.g.: file with __name__ == '__main__')
     */
    protected IPath[] file;

    /**
     * The threads found in the debugger.
     */
    protected PyThread[] threads;

    /**
     * Indicates whether we've already disconnected from the debugger.
     */
    protected boolean disconnected = false;

    /**
     * This is the instance used to pass messages to the debugger.
     */
    protected AbstractRemoteDebugger debugger;

    /**
     * Launch that triggered the debug session.
     */
    protected ILaunch launch;

    /**
     * Class used to check for modifications in the values already found.
     */
    private ValueModificationChecker modificationChecker;

    private PyRunToLineTarget runToLineTarget;

    public AbstractDebugTarget() {
        modificationChecker = new ValueModificationChecker();
    }

    public ValueModificationChecker getModificationChecker() {
        return modificationChecker;
    }

    public abstract boolean canTerminate();

    public abstract boolean isTerminated();

    public void terminate() {

        if (socket != null) {
            try {
                socket.shutdownInput(); // trying to make my pydevd notice that the socket is gone
            } catch (Exception e) {
                // ok, ignore
            }
            try {
                socket.shutdownOutput();
            } catch (Exception e) {
                // ok, ignore
            }
            try {
                socket.close();
            } catch (Exception e) {
                // ok, ignore
            }
        }
        socket = null;
        disconnected = true;

        if (writer != null) {
            writer.done();
            writer = null;
        }
        if (reader != null) {
            reader.done();
            reader = null;
        }

        if (DEBUG) {
            System.out.println("TERMINATE");
        }

        threads = new PyThread[0];
        fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));

    }

    public AbstractRemoteDebugger getDebugger() {
        return debugger;
    }

    public void launchAdded(ILaunch launch) {
        // noop
    }

    public void launchChanged(ILaunch launch) {
        // noop       
    }

    // From IDebugElement
    public String getModelIdentifier() {
        return PyDebugModelPresentation.PY_DEBUG_MODEL_ID;
    }

    // From IDebugElement
    public IDebugTarget getDebugTarget() {
        return this;
    }

    public String getName() throws DebugException {
        if (file != null) {
            return PythonRunnerConfig.getRunningName(file);
        } else {
            return "unknown"; //TODO: SHOW PROPER PROCESS ID!
        }
    }

    public boolean canResume() {
        for (int i = 0; i < threads.length; i++) {
            if (threads[i].canResume()) {
                return true;
            }
        }
        return false;
    }

    public boolean canSuspend() {
        for (int i = 0; i < threads.length; i++) {
            if (threads[i].canSuspend()) {
                return true;
            }
        }
        return false;
    }

    public boolean isSuspended() {
        return false;
    }

    public void resume() throws DebugException {
        for (int i = 0; i < threads.length; i++)
            threads[i].resume();
    }

    public void suspend() throws DebugException {
        for (int i = 0; i < threads.length; i++) {
            threads[i].suspend();
        }
    }

    public IThread[] getThreads() throws DebugException {
        if (debugger == null) {
            return null;
        }

        if (threads == null) {
            ThreadListCommand cmd = new ThreadListCommand(this);
            this.postCommand(cmd);
            try {
                cmd.waitUntilDone(1000);
                threads = cmd.getThreads();
            } catch (InterruptedException e) {
                threads = new PyThread[0];
            }
        }
        return threads;
    }

    public boolean hasThreads() throws DebugException {
        return true;
    }

    //Breakpoints ------------------------------------------------------------------------------------------------------

    /* (non-Javadoc)
     * @see org.python.pydev.debug.model.IExceptionsBreakpointListener#onSetConfiguredExceptions()
     */
    public void onSetConfiguredExceptions() {
        // Sending python exceptions to the debugger
        SendPyExceptionCommand sendCmd = new SendPyExceptionCommand(this);
        this.postCommand(sendCmd);
    }

    /*
     * (non-Javadoc)
     * @see org.python.pydev.debug.model.IPropertyTraceListener#onSetPropertyTraceConfiguration()
     */
    public void onSetPropertyTraceConfiguration() {
        // Sending whether to trace python property
        SetPropertyTraceCommand sendCmd = new SetPropertyTraceCommand(this);
        this.postCommand(sendCmd);
    }

    /**
     * @return true if the given breakpoint is supported by this target
     */
    public boolean supportsBreakpoint(IBreakpoint breakpoint) {
        return breakpoint instanceof PyBreakpoint;
    }

    /**
     * @return true if all the breakpoints should be skipped. Patch from bug:
     * http://sourceforge.net/tracker/index.php?func=detail&aid=1960983&group_id=85796&atid=577329
     */
    private boolean shouldSkipBreakpoints() {
        DebugPlugin manager = DebugPlugin.getDefault();
        return manager != null && !manager.getBreakpointManager().isEnabled();
    }

    /**
     * Adds a breakpoint if it's enabled.
     */
    public void breakpointAdded(IBreakpoint breakpoint) {
        try {
            if (breakpoint instanceof PyBreakpoint) {
                PyBreakpoint b = (PyBreakpoint) breakpoint;
                if (b.isEnabled() && !shouldSkipBreakpoints()) {
                    String condition = null;
                    if (b.isConditionEnabled()) {
                        condition = b.getCondition();
                        if (condition != null) {
                            condition = StringUtils.replaceAll(condition, "\n", "@_@NEW_LINE_CHAR@_@");
                            condition = StringUtils.replaceAll(condition, "\t", "@_@TAB_CHAR@_@");
                        }
                    }
                    SetBreakpointCommand cmd = new SetBreakpointCommand(this, b.getFile(), b.getLine(), condition,
                            b.getFunctionName());
                    this.postCommand(cmd);
                }
            }
        } catch (CoreException e) {
            Log.log(e);
        }
    }

    /**
     * Removes an existing breakpoint from the debug target.
     */
    public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
        if (breakpoint instanceof PyBreakpoint) {
            PyBreakpoint b = (PyBreakpoint) breakpoint;
            RemoveBreakpointCommand cmd = new RemoveBreakpointCommand(this, b.getFile(), b.getLine());
            this.postCommand(cmd);
        }
    }

    /**
     * Called when a breakpoint is changed.
     * E.g.:
     *  - When line numbers change in the file
     *  - When the manager decides to enable/disable all existing markers
     *  - When the breakpoint properties (hit condition) are edited
     */
    public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
        if (breakpoint instanceof PyBreakpoint) {
            breakpointRemoved(breakpoint, null);
            breakpointAdded(breakpoint);
        }
    }

    //End Breakpoints --------------------------------------------------------------------------------------------------

    // Storage retrieval is not supported
    public boolean supportsStorageRetrieval() {
        return false;
    }

    public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
        return null;
    }

    /**
     * When a command that originates from daemon is received,
     * this routine processes it.
     * The responses to commands originating from here
     * are processed by commands themselves
     */
    public void processCommand(String sCmdCode, String sSeqCode, String payload) {
        if (DEBUG) {
            System.out.println("process command:" + sCmdCode +
                    "\tseq:" + sSeqCode +
                    "\tpayload:" + payload +
                    "\n\n");
        }
        try {
            int cmdCode = Integer.parseInt(sCmdCode);

            if (cmdCode == AbstractDebuggerCommand.CMD_THREAD_CREATED) {
                processThreadCreated(payload);

            } else if (cmdCode == AbstractDebuggerCommand.CMD_THREAD_KILL) {
                processThreadKilled(payload);

            } else if (cmdCode == AbstractDebuggerCommand.CMD_THREAD_SUSPEND) {
                processThreadSuspended(payload);

            } else if (cmdCode == AbstractDebuggerCommand.CMD_THREAD_RUN) {
                processThreadRun(payload);

            } else {
                PydevDebugPlugin.log(IStatus.WARNING, "Unexpected debugger command:" + sCmdCode +
                        "\nseq:" + sSeqCode
                        +
                        "\npayload:" + payload, null);
            }
        } catch (Exception e) {
            PydevDebugPlugin.log(IStatus.ERROR, "Error processing: " + sCmdCode +
                    "\npayload: " + payload, e);
        }
    }

    public void fireEvent(DebugEvent event) {
        DebugPlugin manager = DebugPlugin.getDefault();
        if (manager != null) {
            manager.fireDebugEventSet(new DebugEvent[] { event });
        }
    }

    /**
     * @return an existing thread with a given id (null if none)
     */
    protected PyThread findThreadByID(String thread_id) {
        for (IThread thread : threads) {
            if (thread_id.equals(((PyThread) thread).getId())) {
                return (PyThread) thread;
            }
        }
        return null;
    }

    /**
     * Add it to the list of threads
     */
    private void processThreadCreated(String payload) {

        PyThread[] newThreads;
        try {
            newThreads = XMLUtils.ThreadsFromXML(this, payload);
        } catch (CoreException e) {
            PydevDebugPlugin.errorDialog("Error in processThreadCreated", e);
            return;
        }

        // Hide Pydevd threads if requested
        if (PydevDebugPlugin.getDefault().getPreferenceStore()
                .getBoolean(PydevDebugPreferencesInitializer.HIDE_PYDEVD_THREADS)) {
            int removeThisMany = 0;

            for (int i = 0; i < newThreads.length; i++) {
                if (((PyThread) newThreads[i]).isPydevThread()) {
                    removeThisMany++;
                }
            }

            if (removeThisMany > 0) {
                int newSize = newThreads.length - removeThisMany;

                if (newSize == 0) { // no threads to add
                    return;

                } else {

                    PyThread[] newnewThreads = new PyThread[newSize];
                    int i = 0;

                    for (PyThread newThread : newThreads) {
                        if (!((PyThread) newThread).isPydevThread()) {
                            newnewThreads[i] = newThread;
                            i += 1;
                        }
                    }

                    newThreads = newnewThreads;

                }
            }
        }

        // add threads to the thread list, and fire event
        if (threads == null) {
            threads = newThreads;

        } else {
            PyThread[] combined = new PyThread[threads.length + newThreads.length];
            int i = 0;
            for (i = 0; i < threads.length; i++) {
                combined[i] = threads[i];
            }

            for (int j = 0; j < newThreads.length; i++, j++) {
                combined[i] = newThreads[j];
            }
            threads = combined;
        }
        // Now notify debugger that new threads were added
        for (int i = 0; i < newThreads.length; i++) {
            fireEvent(new DebugEvent(newThreads[i], DebugEvent.CREATE));
        }
    }

    // Remote this from our thread list
    private void processThreadKilled(String thread_id) {
        PyThread threadToDelete = findThreadByID(thread_id);
        if (threadToDelete != null) {
            int j = 0;
            PyThread[] newThreads = new PyThread[threads.length - 1];
            for (int i = 0; i < threads.length; i++) {
                if (threads[i] != threadToDelete) {
                    newThreads[j++] = threads[i];
                }
            }
            threads = newThreads;
            fireEvent(new DebugEvent(threadToDelete, DebugEvent.TERMINATE));
        }
    }

    private void processThreadSuspended(String payload) {
        Object[] threadNstack;
        try {
            threadNstack = XMLUtils.XMLToStack(this, payload);
        } catch (CoreException e) {
            PydevDebugPlugin.errorDialog("Error reading ThreadSuspended", e);
            return;
        }

        PyThread t = (PyThread) threadNstack[0];
        int reason = DebugEvent.UNSPECIFIED;
        String stopReason = (String) threadNstack[1];

        if (stopReason != null) {
            int stopReason_i = Integer.parseInt(stopReason);

            if (stopReason_i == AbstractDebuggerCommand.CMD_STEP_OVER
                    || stopReason_i == AbstractDebuggerCommand.CMD_STEP_INTO
                    || stopReason_i == AbstractDebuggerCommand.CMD_STEP_RETURN
                    || stopReason_i == AbstractDebuggerCommand.CMD_RUN_TO_LINE
                    || stopReason_i == AbstractDebuggerCommand.CMD_SET_NEXT_STATEMENT) {
                reason = DebugEvent.STEP_END;

            } else if (stopReason_i == AbstractDebuggerCommand.CMD_THREAD_SUSPEND) {
                reason = DebugEvent.CLIENT_REQUEST;

            } else if (stopReason_i == AbstractDebuggerCommand.CMD_SET_BREAK) {
                reason = DebugEvent.BREAKPOINT;

            } else {
                PydevDebugPlugin.log(IStatus.ERROR, "Unexpected reason for suspension", null);
                reason = DebugEvent.UNSPECIFIED;
            }
        }
        if (t != null) {
            modificationChecker.onlyLeaveThreads((PyThread[]) this.threads);

            IStackFrame stackFrame[] = (IStackFrame[]) threadNstack[2];
            t.setSuspended(true, stackFrame);
            fireEvent(new DebugEvent(t, DebugEvent.SUSPEND, reason));
        }
    }

    /**
     * @param payload a string in the format: thread_id\tresume_reason
     * E.g.: pid3720_zad_seq1\t108
     * 
     * @return a tuple with the thread id and the reason it stopped.
     * @throws CoreException
     */
    public static Tuple<String, String> getThreadIdAndReason(String payload) throws CoreException {
        List<String> split = StringUtils.split(payload.trim(), '\t');
        if (split.size() != 2) {
            String msg = "Unexpected threadRun payload " + payload +
                    "(unable to match)";
            throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, msg, new RuntimeException(msg)));
        }
        return new Tuple<String, String>(split.get(0), split.get(1));
    }

    /**
     * ThreadRun event processing
     */
    private void processThreadRun(String payload) {
        try {
            Tuple<String, String> threadIdAndReason = getThreadIdAndReason(payload);
            int resumeReason = DebugEvent.UNSPECIFIED;
            try {
                int raw_reason = Integer.parseInt(threadIdAndReason.o2);
                if (raw_reason == AbstractDebuggerCommand.CMD_STEP_OVER)
                    resumeReason = DebugEvent.STEP_OVER;
                else if (raw_reason == AbstractDebuggerCommand.CMD_STEP_RETURN)
                    resumeReason = DebugEvent.STEP_RETURN;
                else if (raw_reason == AbstractDebuggerCommand.CMD_STEP_INTO)
                    resumeReason = DebugEvent.STEP_INTO;
                else if (raw_reason == AbstractDebuggerCommand.CMD_RUN_TO_LINE)
                    resumeReason = DebugEvent.UNSPECIFIED;
                else if (raw_reason == AbstractDebuggerCommand.CMD_SET_NEXT_STATEMENT)
                    resumeReason = DebugEvent.UNSPECIFIED;
                else if (raw_reason == AbstractDebuggerCommand.CMD_THREAD_RUN)
                    resumeReason = DebugEvent.CLIENT_REQUEST;
                else {
                    PydevDebugPlugin.log(IStatus.ERROR, "Unexpected resume reason code", null);
                    resumeReason = DebugEvent.UNSPECIFIED;
                }
            } catch (NumberFormatException e) {
                // expected, when pydevd reports "None"
                resumeReason = DebugEvent.UNSPECIFIED;
            }

            String threadID = threadIdAndReason.o1;
            PyThread t = (PyThread) findThreadByID(threadID);
            if (t != null) {
                t.setSuspended(false, null);
                fireEvent(new DebugEvent(t, DebugEvent.RESUME, resumeReason));

            } else {
                FastStringBuffer buf = new FastStringBuffer();
                for (PyThread thread : threads) {
                    if (buf.length() > 0) {
                        buf.append(", ");
                    }
                    buf.append("id: " + thread.getId());
                }
                String msg = "Unable to find thread: " + threadID +
                        " available: " + buf;
                PydevDebugPlugin.log(IStatus.ERROR, msg, new RuntimeException(msg));
            }
        } catch (CoreException e1) {
            Log.log(e1);
        }

    }

    /**
     * Called after debugger has been connected.
     *
     * Here we send all the initialization commands
     * and exceptions on which pydev debugger needs to break
     */
    public void initialize() {
        // we post version command just for fun
        // it establishes the connection
        this.postCommand(new VersionCommand(this));

        // now, register all the breakpoints in all projects
        addBreakpointsFor(ResourcesPlugin.getWorkspace().getRoot());

        // Sending python exceptions and property trace state before sending run command
        this.onSetConfiguredExceptions();
        this.onSetPropertyTraceConfiguration();

        // Send the run command, and we are off
        RunCommand run = new RunCommand(this);
        this.postCommand(run);
    }

    /**
     * Adds the breakpoints associated with a container.
     * @param container the container we're interested in (usually workspace root)
     */
    private void addBreakpointsFor(IContainer container) {
        try {
            IMarker[] markers = container.findMarkers(PyBreakpoint.PY_BREAK_MARKER, true, IResource.DEPTH_INFINITE);
            IMarker[] condMarkers = container.findMarkers(PyBreakpoint.PY_CONDITIONAL_BREAK_MARKER, true,
                    IResource.DEPTH_INFINITE);
            IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager();

            for (IMarker marker : markers) {
                PyBreakpoint brk = (PyBreakpoint) breakpointManager.getBreakpoint(marker);
                breakpointAdded(brk);
            }

            for (IMarker marker : condMarkers) {
                PyBreakpoint brk = (PyBreakpoint) breakpointManager.getBreakpoint(marker);
                breakpointAdded(brk);
            }
        } catch (Throwable t) {
            PydevDebugPlugin.errorDialog("Error setting breakpoints", t);
        }
    }

    /**
     * This function adds the input listener extension point, so that plugins that only care about
     * the input in the console can know about it.
     */
    @SuppressWarnings({ "unchecked" })
    public void addConsoleInputListener() {
        IConsole console = DebugUITools.getConsole(this.getProcess());
        if (console instanceof ProcessConsole) {
            final ProcessConsole c = (ProcessConsole) console;
            final List<IConsoleInputListener> participants = ExtensionHelper
                    .getParticipants(ExtensionHelper.PYDEV_DEBUG_CONSOLE_INPUT_LISTENER);
            final AbstractDebugTarget target = this;
            //let's listen the doc for the changes
            c.getDocument().addDocumentListener(new IDocumentListener() {

                public void documentAboutToBeChanged(DocumentEvent event) {
                    //only report when we have a new line
                    if (event.fText.indexOf('\r') != -1 || event.fText.indexOf('\n') != -1) {
                        try {
                            ITypedRegion partition = event.fDocument.getPartition(event.fOffset);
                            if (partition instanceof IOConsolePartition) {
                                IOConsolePartition p = (IOConsolePartition) partition;

                                //we only communicate about inputs (because we only care about what the user writes)
                                if (p.getType().equals(IOConsolePartition.INPUT_PARTITION_TYPE)) {
                                    if (event.fText.length() <= 2) {
                                        //the user typed something
                                        final String inputFound = p.getString();
                                        for (IConsoleInputListener listener : participants) {
                                            listener.newLineReceived(inputFound, target);
                                        }
                                    }

                                }
                            }
                        } catch (Exception e) {
                            Log.log(e);
                        }
                    }

                }

                public void documentChanged(DocumentEvent event) {
                    //only report when we have a new line
                    if (event.fText.indexOf('\r') != -1 || event.fText.indexOf('\n') != -1) {
                        try {
                            ITypedRegion partition = event.fDocument.getPartition(event.fOffset);
                            if (partition instanceof IOConsolePartition) {
                                IOConsolePartition p = (IOConsolePartition) partition;

                                //we only communicate about inputs (because we only care about what the user writes)
                                if (p.getType().equals(IOConsolePartition.INPUT_PARTITION_TYPE)) {
                                    if (event.fText.length() > 2) {
                                        //the user pasted something
                                        for (IConsoleInputListener listener : participants) {
                                            listener.pasteReceived(event.fText, target);
                                        }
                                    }

                                }
                            }
                        } catch (Exception e) {
                            Log.log(e);
                        }
                    }
                }

            });
        }
    }

    public boolean canDisconnect() {
        return !disconnected;
    }

    public void disconnect() throws DebugException {
        this.terminate();
        modificationChecker = null;
    }

    public boolean isDisconnected() {
        return disconnected;
    }

    public Object getAdapter(Class adapter) {
        AdapterDebug.print(this, adapter);

        // Not really sure what to do here, but I am trying
        if (adapter.equals(ILaunch.class)) {
            return launch;

        } else if (adapter.equals(IResource.class)) {
            // used by Variable ContextManager, and Project:Properties menu item
            if (file != null) {
                IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocation(file[0]);

                if (files != null && files.length > 0) {
                    return files[0];

                } else {
                    return null;

                }
            }

        } else if (adapter.equals(org.eclipse.debug.ui.actions.IRunToLineTarget.class)) {
            return this.getRunToLineTarget();

        } else if (adapter.equals(IPropertySource.class)) {
            return launch.getAdapter(adapter);

        } else if (adapter.equals(ITaskListResourceAdapter.class)
                || adapter.equals(org.eclipse.debug.ui.actions.IToggleBreakpointsTarget.class)) {
            return super.getAdapter(adapter);
        }

        AdapterDebug.printDontKnow(this, adapter);
        return super.getAdapter(adapter);
    }

    public PyRunToLineTarget getRunToLineTarget() {
        if (this.runToLineTarget == null) {
            this.runToLineTarget = new PyRunToLineTarget();
        }
        return this.runToLineTarget;
    }

    //From IDebugElement
    public ILaunch getLaunch() {
        return launch;
    }

}
TOP

Related Classes of org.python.pydev.debug.model.AbstractDebugTarget

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.